/**
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 *   
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 */
package org.jiopi.ibean.bootstrap;

import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Properties;
import java.util.logging.Level;
import org.jiopi.framework.core.CentralConsoleKernel;
import org.jiopi.framework.core.initializer.Initializer;
import org.jiopi.ibean.bootstrap.util.DirLoker;
import org.jiopi.ibean.loader.log.LoaderLogUtil;
import org.jiopi.ibean.share.ShareConstants;
import org.jiopi.ibean.share.ShareUtil.ClassUtil;
import org.jiopi.ibean.share.ShareUtil.FileUtil;
import org.jiopi.ibean.share.ShareUtil.ResourceUtil;

/**
 * 引导程序的初始化工具
 * 
 * @since 2010.4.11
 *
 */
public class IBeanInitializer implements Initializer {
	
	private static volatile boolean initialized = false;
	
	/**
	 * 如果系统支持hotSwap,INS将被赋值
	 */
	protected static volatile Instrumentation INS = null;
	
	public void initialize() {
		bootstrapInitialize();
		try{
			Method init = IBeanCenterConsoleKernelLoader.cck.getClass().getDeclaredMethod( "init", new Class<?>[]{} );
			init.invoke(IBeanCenterConsoleKernelLoader.cck, new Object[]{} );
		}catch(Exception e){
			LoaderLogUtil.logExceptionTrace(BootstrapConstants.bootstrapLogger, Level.WARNING, e);
		}
		
	}


	public synchronized static void bootstrapInitialize() {
		if(!initialized){
			BootstrapConstants.bootstrapLogger.info(BootstrapConstants.COPY_RIGHT);
			String iBeanProgramPath = ResourceUtil.getIBeanProgramDir();
			String[] vers = FileUtil.getVersionsDirName(iBeanProgramPath);
			if(vers == null || vers.length<1){
				BootstrapConstants.bootstrapLogger.warning("not found iBean program,Initializer not finished");
				return;
			}
			
			String ibeanProgramVersionPath = FileUtil.joinPath(iBeanProgramPath,vers[0]);
			
			//初始化HotSwap支持
			initHotSwap(ibeanProgramVersionPath);
			
			//初始化IBeanCenterConsoleKernel
			initIBeanCenterConsoleKernel(ibeanProgramVersionPath);
			
			
			//监听系统结束
			Runtime.getRuntime().addShutdownHook( new Thread() {
				public void run() {
					CloseableManager.close();
					BootstrapConstants.bootstrapLogger.info("iBean Framework Closed.");
				}
			} );
			
			initialized = true;
		}
	}
	
	private static void initIBeanCenterConsoleKernel(String ibeanProgramVersionPath){
		
		//获取当前workDir
		Properties[] jiopiProperties = ResourceUtil.getJIOPICascadingConfig();
		String workDir = ResourceUtil.getPropertyValue(ShareConstants.IBEAN_WORK_DIR, jiopiProperties,ShareConstants.IBEAN_WORK_DIR_DEFAULT, false);
		workDir = FileUtil.getPathWithSystemProperty(workDir);
		
		String kernelDirPath = FileUtil.joinPath(workDir,BootstrapConstants.KERNEL_DIR_PATH);
		//获得唯一锁定的 kernelDir
		File kernelDir = null;
		try {
			kernelDir = DirLoker.getUniqueDirLock(kernelDirPath);
		} catch (IOException e) {
			LoaderLogUtil.logExceptionTrace(BootstrapConstants.bootstrapLogger, Level.WARNING, e);
		}
		if(kernelDir==null||!kernelDir.isDirectory()){
			BootstrapConstants.bootstrapLogger.severe("Can't lock a kernel Dir!");
			throw new RuntimeException("Can't lock a kernel Dir!");
		}
		BootstrapConstants.bootstrapLogger.config("use kernel work dir "+kernelDir.getAbsolutePath());
		
		KernelClose kc = new KernelClose(kernelDir);
		
		CloseableManager.register(kc);
		
		//创建KernelClassLoader
		ClassLoader parentClassLoader = ClassUtil.getClassLoaderByClass(IBeanInitializer.class);
		URL[] kernelShareJars = new URL[0];
		if(!ShareConstants.debugMode){
			//载入kernel-share安装目录的jar
			String kernelProgramPath = FileUtil.joinPath(ibeanProgramVersionPath,BootstrapConstants.KERNEL_DIR_PATH);
			BootstrapConstants.bootstrapLogger.config("load kernel share from "+kernelProgramPath);
			File kernelProgramDir = new File(kernelProgramPath);
			kernelShareJars = FileUtil.toURL(kernelProgramDir.listFiles());
		}
		ClassLoader kernelShareClassLoader = new KernelClassLoader(kernelDir.getAbsolutePath(),kernelShareJars,parentClassLoader);
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		try{
			Thread.currentThread().setContextClassLoader( kernelShareClassLoader );
			//生成iBeanCentralConsoleKernel对象
			Class<?> cckClass=null;
			try {
				cckClass = kernelShareClassLoader.loadClass(BootstrapConstants.KERNEL_SHARE_CCK_CLASS);
			} catch (ClassNotFoundException e) {
				LoaderLogUtil.logExceptionTrace(BootstrapConstants.bootstrapLogger, Level.SEVERE, e, "load Kernel Class ERROR!");
				throw new RuntimeException("load Kernel Class ERROR!",e);
			}
			try {
				IBeanCenterConsoleKernelLoader.cck = (CentralConsoleKernel) cckClass.newInstance();
			} catch (Exception e) {
				LoaderLogUtil.logExceptionTrace(BootstrapConstants.bootstrapLogger, Level.SEVERE, e, "load Kernel Class ERROR!");
				throw new RuntimeException("load Kernel Class ERROR!",e);
			}
			//调用iBeanCentralConsoleKernel的init方法初始化,传递 Instrumentation
			try{
				Method init = IBeanCenterConsoleKernelLoader.cck.getClass().getDeclaredMethod( "init", new Class<?>[]{Instrumentation.class} );
				init.invoke(IBeanCenterConsoleKernelLoader.cck, new Object[]{INS} );
			}catch(Exception e){
				LoaderLogUtil.logExceptionTrace(BootstrapConstants.bootstrapLogger, Level.WARNING, e);
			}

			BootstrapConstants.bootstrapLogger.info("iBean Kernel is Ready!");
		}finally{
			Thread.currentThread().setContextClassLoader( contextClassLoader );
		}
	}
	
	private static void initHotSwap(String ibeanProgramVersionPath){
		String hotswapagentPath = FileUtil.joinPath(ibeanProgramVersionPath,BootstrapConstants.HOTSWAPAGENT_DIR_PATH);
		File hotswapagentDir = new File(hotswapagentPath);
		String hotswapagentJarPath = null;
		String[] files = hotswapagentDir.list();
		for(String file:files){
			if(file.toLowerCase().endsWith(".jar")) hotswapagentJarPath = FileUtil.joinPath(hotswapagentPath,file);
		}
		if(hotswapagentJarPath!=null){
			BootstrapConstants.bootstrapLogger.config("Load "+hotswapagentJarPath);
			/*
			 * 载入hotswapagent
			 */
			try {
				Class<?> VirtualMachineClass = null;
				try{
					VirtualMachineClass = IBeanInitializer.class.getClassLoader().loadClass("com.sun.tools.attach.VirtualMachine");
				}catch(Exception e){
				}
				if(VirtualMachineClass!=null){
					Object vm = method("attach",VirtualMachineClass,null,new Class<?>[]{String.class},ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
					method("loadAgent",VirtualMachineClass,vm,new Class<?>[]{String.class},hotswapagentJarPath);
					method("detach",VirtualMachineClass,vm,new Class<?>[]{});
					
					//检测hotSwap的支持
					Class<?> hotSwapAgentClass = Class.forName(BootstrapConstants.HOT_SWAP_AGENT_CLASS);
					Instrumentation instrumentation = (Instrumentation)method("getInstrumentation",hotSwapAgentClass,null,new Class[0]);
					INS = instrumentation;
					try{
						method("isRetransformClassesSupported",Instrumentation.class,instrumentation,new Class<?>[]{});
						BootstrapConstants.bootstrapLogger.info("iBean Hotswap Enabled!");
					}catch(Exception e){
						BootstrapConstants.bootstrapLogger.info("iBean Hotswap Disabled! Please use JDK1.6 to enable.");
					}
				}
			} catch (Exception e) {
				LoaderLogUtil.logExceptionTrace(BootstrapConstants.bootstrapLogger, Level.WARNING, null, "iBean initialization Error! Some function may be unavailable(HotSwap,Redefine Class...).");
			}
		}else{
			BootstrapConstants.bootstrapLogger.warning("not found iBean program,initialization is not finished");
		}
	}
	
	private static Object method(String name,Class<?> clazz,Object obj,Class<?>[] parameterTypes,Object... args) throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException, IllegalAccessException{
		Method method = clazz.getMethod(name, parameterTypes);
		return method.invoke(obj, args);
	}
	
	private static class KernelClose implements Closeable{
		private final File kernelDir;
		public KernelClose(File kernelDir){
			this.kernelDir = kernelDir;
		}
		
		public void close(){
			//删除临时目录
			if(kernelDir.isDirectory()){
				File tmpDir = new File(kernelDir,BootstrapConstants.TMP_DIR);
				FileUtil.deleteFile(tmpDir);
				BootstrapConstants.bootstrapLogger.info("tmpDir "+tmpDir+" deleted.");
			}
		}
	}
	
}
